#pragma once
#include <vector>
#include <unordered_map>
#include <functional>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "InetAddr.hpp"
#include "Log.hpp"

const std::string login = "login";
const std::string logout = "logout";
const std::string route = "route";
const std::string esp = ": ";
using func_t = std::function<void(const std::string &, const InetAddr &, int)>;
using iterator = std::vector<std::pair<InetAddr, std::string>>::iterator;
using namespace LogModule;
using namespace MutexModule;

class Router
{
private:
    iterator FindUser(const InetAddr &client)
    {
        iterator it;
        for (it = _online_users.begin(); it != _online_users.end(); it++)
        {
            if (it->first == client)
                break;
        }
        return it;
    }
    iterator FindName(const std::string &name)
    {
        iterator it;
        for (it = _online_users.begin(); it != _online_users.end(); it++)
        {
            if (it->second == name)
                break;
        }
        return it;
    }
    void SendToClient(const std::string &context, const InetAddr &client, int sockfd)
    {
        sendto(sockfd, context.c_str(), context.size(), 0, (struct sockaddr *)&client.NetAddr(), sizeof(client.NetAddr()));
    }
    void RouteToClients(const std::string &context, int sockfd)
    {
        for (auto &user : _online_users)
        {
            SendToClient(context, user.first, sockfd);
        }
    }

public:
    Router()
    {
        _methods[login] = [this](const std::string &context, const InetAddr &client, int sockfd)
        {
            std::string info;
            if (FindUser(client) != _online_users.end())
            {
                info = "用户[" + client.Info() + "]重复加入";
                LOG(LogLevel::WARNING) << info;
                SendToClient(context, client, sockfd);
            }
            else if (FindName(context) != _online_users.end())
            {
                info = "昵称[" + context + "]已存在";
                LOG(LogLevel::WARNING) << info;
                SendToClient(context, client, sockfd);
            }
            else
            {
                _mutex.lock();
                _online_users.emplace_back(client, context);
                _mutex.unlock();

                info = "用户[" + context + "]加入聊天";
                LOG(LogLevel::INFO) << info;
                // 登录成功，回复客户端
                SendToClient(login, client, sockfd);
                RouteToClients(info, sockfd);
            }
        };
        _methods[logout] = [this](const std::string &context, const InetAddr &client, int sockfd)
        {
            auto it = FindUser(client);
            if (it == _online_users.end())
            {
                std::string info = "用户[" + client.Info() + "]在未登录的情况下登出! ";
                LOG(LogLevel::WARNING) << info;
                SendToClient(info, client, sockfd);
                return;
            }

            _mutex.lock();
            _online_users.erase(it);
            _mutex.unlock();
            
            std::string info = "用户[" + it->second + "]离开聊天";
            LOG(LogLevel::INFO) << info;
            SendToClient(logout, client, sockfd);
            RouteToClients(info, sockfd);
        };
        _methods[route] = [this](const std::string &context, const InetAddr &client, int sockfd) {
            auto user = FindUser(client);
            if (user == _online_users.end())
            {
                std::string info = "用户还未登录, 无法发送消息: " + context;
                LOG(LogLevel::ERROR) << info;
                SendToClient(info, client, sockfd);
                return;
            }
            std::string info = "[" + user->second + "]# " + context;
            RouteToClients(info, sockfd);
        };
    }

    void RouteMessage(const std::string message, const InetAddr &client, int sockfd)
    {
        auto pos = message.find(esp);
        if (pos == std::string::npos)
        {
            std::string info = "客户端信息格式错误[" + message + "]";
            LOG(LogLevel::ERROR) << info;
            SendToClient(info, client, sockfd);
            return;
        }
        std::string type = message.substr(0, pos);
        std::string context = message.substr(pos + esp.size());
        if (type.empty() || !_methods.count(type))
        {
            std::string info = "错误的消息类型[" + type + "]";
            LOG(LogLevel::ERROR) << info;
            SendToClient(info, client, sockfd);
            return;
        }
        _methods[type](context, client, sockfd);
    }

private:
    std::vector<std::pair<InetAddr, std::string>> _online_users;
    std::unordered_map<std::string, func_t> _methods;
    Mutex _mutex;
};